---
description: The Plasmo messaging API is a powerful tool for sending real-time messages between different parts of your extension.
---

import { Callout } from "nextra-theme-docs"

import { ShieldBanners } from "~components/shield-banners"

# Messaging API

<ShieldBanners packageName="@plasmohq/messaging" />

Plasmo's Messaging API makes communication between different parts of your extension easy. Add a file to your `messages` directory, and Plasmo will handle all the rest. Plasmo Messaging is a declarative, type-safe, functional, promise-based API for sending, relaying, and receiving messages between your extension components.

## Installation

```
pnpm install @plasmohq/messaging
```

To use this library, you must use a directory for your background service worker. So if you've been adding it to your root, replace `background.ts` with `background/index.ts`

## TL;DR

| Messaging API                 | From         | To           | One-time | Long-lived |
| :---------------------------- | :----------- | :----------- | :------- | :--------- |
| [Message Flow](#message-flow) | Ext-Pages/CS | BGSW         | Yes      | No         |
| [Relay Flow](#relay-flow)     | Website      | CS/BGSW      | Yes      | No         |
| [Ports](#ports)               | Ext-Pages/CS | BGSW         | No       | Yes        |
| Ports                         | BGSW         | Ext-Pages/CS | No       | Yes        |
| Ports + Relay                 | BGSW         | WebPage      | Yes      | Yes        |

## Examples

- [with-messaging](https://github.com/PlasmoHQ/examples/tree/main/with-messaging)

## Message Flow

Use the Message Flow to initiate one-time messages between extension pages, tab pages or content scripts with the background service worker. This flow is useful to offload heavy computation to the background service worker or to bypass CORS.

The background service worker is a message hub with REST-style API handlers. To create a message handler, create a ts module in the `background/messages` directory. The file name should be the message name, and the default export should be the handler function:

```ts filename="background/messages/ping.ts"
import type { PlasmoMessaging } from "@plasmohq/messaging"

const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
  const message = await querySomeApi(req.body.id)

  res.send({
    message
  })
}

export default handler
```

Extension pages, content scripts, or tab pages can send messages to these handlers using the `@plasmohq/messaging` library. Since Plasmo Framework orchestrates your handlers behind the scenes, the message names are typed and will enable IntelliSense in your editor:

```tsx filename="popup.tsx"
import { sendToBackground } from "@plasmohq/messaging"

...
const resp = await sendToBackground({
  name: "ping",
  body: {
    id: 123
  }
})

console.log(resp)
```

To send a message from a content script thats in the main world you'll have to include your extension's id in the request. Your extension's id can be found in chrome's extension manager window once you've built and added it to your browser.

```tsx filename="contents/componentInTheMainWorld.tsx"
import { sendToBackground } from "@plasmohq/messaging"
import type { PlasmoCSConfig } from "plasmo"

export const config: PlasmoCSConfig = {
  matches: ["<all_urls>"],
  world: "MAIN"
}
...
const resp = await sendToBackground({
  name: "ping",
  body: {
    id: 123
  },
  extensionId: 'llljfehhnoeipgngggpomjapaakbkyyy' // find this in chrome's extension manager
})

console.log(resp)
```


## Relay Flow

<Callout emoji="⚠️">

**NOTE:** The Relay messaging API is in public alpha preview: expects bugs, incomplete/leaky abstractions, and future API changes. Please report any issues you encounter [to us via this link](/bug).

</Callout>

Use the Relay Flow to communicate between a target webpage and the background service worker. A relay is a lightweight message handler registered using a [content script](/framework/content-scripts). It listens for messages from the target webpage and pipes them down to the [Message Flow's handlers](#message-flow).

Create a relay inside a content script. The `relayMessage` function takes a message name. A content script can have multiple relays. Given the `ping` message handler from the previous example, and the website `www.plasmo.com`:

```ts filename="contents/plasmo.ts"
import type { PlasmoCSConfig } from "plasmo"

import { relayMessage } from "@plasmohq/messaging"

export const config: PlasmoCSConfig = {
  matches: ["http://www.plasmo.com/*"] // Only relay messages from this domain
}

relayMessage({
  name: "ping"
})
```

- On the `plasmo.com` web page, you can send messages via the relay:

```tsx filename="pages/index.tsx"

import { sendToBackgroundViaRelay } from "@plasmohq/messaging"
...

const resp = await sendToBackgroundViaRelay({
  name: "ping"
})

console.log(resp)
```

To relay messages from contexts where `chrome.runtime` is unavailable, you can use the `relay` function:

```tsx filename="sandbox.tsx"
import { relayMessage } from "@plasmohq/messaging/relay"

relayMessage(
  {
    name: "ping"
  },
  async (req) => {
    console.log("some message was relayed:", req)

    return {
      message: "Hello from sandbox"
    }
  }
)
```

## Ports

<Callout emoji="⚠️">

The Port messaging API is in public alpha preview: expects bugs, incomplete/leaky abstractions, and future API changes. Please report any issues you encounter [to us via this link](/bug).

</Callout>

The Messaging Ports API is a high-level abstraction over the chrome runtime's [port API](https://developer.chrome.com/docs/extensions/mv3/messaging/#connect) to establish long-lived connections with the background service worker.

The current implementation focuses on establishing connections to a port listener in the background service worker:

To create a BGSW port handler, create a ts module in the `background/ports` directory. The file name will be the port name, and the default export will be the handler function:

```ts filename="background/ports/mail.ts"
import type { PlasmoMessaging } from "@plasmohq/messaging"

const handler: PlasmoMessaging.PortHandler = async (req, res) => {
  console.log(req)

  res.send({
    message: "Hello from port handler"
  })
}

export default handler
```

In your extension page, get the port using the `getPort` utility under the `@plasmohq/messaging/port`, **OR** use the `usePort` hook, keep in mind that `usePort` currently relies on React hooks so you will need to use it within a React component. This example shows the usage of `getPort` within a Svelte component:

```svelte filename="popup.svelte"
<script lang="ts">
  import { getPort } from "@plasmohq/messaging/port"
  import { onMount, onDestroy } from "svelte"

  let output = ""

  const mailPort = getPort("mail")

  onMount(() => {
    mailPort.onMessage.addListener((msg) => {
      output = msg
    })
  })

  onDestroy(() => {
    mailPort.onMessage.removeListener((msg) => {
      output = msg
    })
  })

  function handleSubmit() {
    mailPort.postMessage({
      body: {
        hello: "world"
      }
    })
  }
</script>

<div>{output}</div>
```

Here's an example of `usePort` in React, the data will always reflect the latest response from the port handler:

```tsx filename="tabs/delta.tsx"
import { usePort } from "@plasmohq/messaging/hook"

function DeltaTab() {
  const mailPort = usePort("mail")

  return (
    <div>
      {mailPort.data?.message}
      <button
        onClick={async () => {
          mailPort.send({
            hello: "world"
          })
        }}>
        Send Data
      </button>
    </div>
  )
}

export default DeltaTab
```

## Initial Type Error

If you're receiving a type error such as `"name" is never`, this is because Plasmo needs to compile your handler types. To resolve:

1. Run the dev server
2. Restart the TypeScript server in your editor

## E2E Type Safety (WIP)

End-to-end request/response body type-safety is in progress at [#334](https://github.com/PlasmoHQ/plasmo/issues/334). In the meantime, you can use the provided generic types:

```ts filename="background/messages/ping.ts"
import type { PlasmoMessaging } from "@plasmohq/messaging"

export type RequestBody = {
  id: number
}

export type ResponseBody = {
  message: string
}

const handler: PlasmoMessaging.MessageHandler<
  RequestBody,
  ResponseBody
> = async (req, res) => {
  console.log(req.body.id)

  res.send({
    message: "Hello from background"
  })
}

export default handler
```

```ts filename="popup.tsx"
import { sendToBackground } from "@plasmohq/messaging"

import type { RequestBody, ResponseBody } from "~background/messages/ping"

...

const resp = await sendToBackground<RequestBody, ResponseBody>({
  name: "ping",
  body: {
    id: 123
  }
})

console.log(resp)
```
